﻿namespace CodeBetter.Extensions
{
   using System;
   using System.Globalization;
   using System.Text.RegularExpressions;
   using System.Resources;

  public static class StringExtensions
  {

    private static readonly Regex _htmlTagPattern = new Regex(@"<\/?[^>]*>", RegexOptions.Compiled | RegexOptions.Multiline);
    private static readonly Regex _multipleSpaces = new Regex(@"\s{2,}", RegexOptions.Compiled | RegexOptions.Multiline);
    private static readonly Regex _noSpaceAfterPunctuation = new Regex(@"([\.\?\!,:;\-]""?)(\S)", RegexOptions.Compiled | RegexOptions.Multiline);
    private static readonly Regex _firstLowerCaseLetterAfterSentence = new Regex(@"(\.\s*)([a-z])", RegexOptions.Compiled | RegexOptions.Multiline);
    private static readonly Regex _lowercaseI = new Regex(@"([\s\p{P}])i([\s\p{P}])", RegexOptions.Compiled | RegexOptions.Multiline);


    /// <summary>
    /// Compares a string to a given string. The comparison is case insensitive.
    /// </summary>      
    /// <param name="compareTo">The string to compare against</param>
    /// <returns>True if the strings are the same, false otherwise.</returns>
    public static bool Is(this string @string, string compareTo)
    {
      return string.Compare(@string, compareTo, true) == 0;
    }

    /// <summary>
    /// Creates a type from the given name
    /// </summary>
    /// <typeparam name="T">The type being created</typeparam>      
    /// <param name="args">Arguments to pass into the constructor</param>
    /// <returns>An instance of the type</returns>
    public static T CreateType<T>(this string typeName, params object[] args)
    {
      Type type = Type.GetType(typeName, true, true);
      return (T)Activator.CreateInstance(type, args);
    }

    /// <summary>
    /// Replaces each newline with a &lt;br /&gt; tag
    /// </summary>      
    public static string NewlineToBr(this string html)
    {
      return html.Replace(Environment.NewLine, "<br />");
    }

    /// <summary>
    /// Removes html tags from a given strnig
    /// </summary>      
    public static string StripHtml(this string html)
    {
      return _htmlTagPattern.Replace(html, "");
    }

    /// <summary>
    /// Removes extra spaces within a string
    /// </summary>      
    public static string Strip(this string @string)
    {
      return _multipleSpaces.Replace(@string, " ");
    }

    /// <summary>
    /// Fixes a paragraph so that it more properly conforms to english rules (single space after punctuation,
    /// capitalization and so on).
    /// </summary>      
    public static string Proper(this string paragraph)
    {
      paragraph = _noSpaceAfterPunctuation.Replace(paragraph, "$1 $2").Strip();
      paragraph = _firstLowerCaseLetterAfterSentence.Replace(paragraph, m => m.Groups[1].Value + m.Groups[2].Value.ToUpper());
      paragraph = paragraph.Substring(0, 1).ToUpper() + paragraph.Substring(1);
      paragraph = _lowercaseI.Replace(paragraph, "$1I$2");
      return paragraph;
    }

    /// <summary>
    /// Reverse the order of a string
    /// </summary>      
    public static string Reverse(this string @string)
    {
      var reversed = @string.ToCharArray();
      Array.Reverse(reversed);
      return new string(reversed);
    }

    /// <summary>
    /// Applies formatting to the specified string
    /// </summary>      
    public static string FormatWith(this string @string, params object[] args)
    {
      return string.Format(@string, args);
    }

    /// <summary>
    /// Capitalizes the first letter of a string
    /// </summary>      
    public static string Capitalize(this string @string)
    {
      if (@string.Length == 0)
      {
        return @string;
      }
      if (@string.Length == 1)
      {
        return @string.ToUpper(CultureInfo.InvariantCulture);
      }
      return @string.Substring(0, 1).ToUpper(CultureInfo.InvariantCulture) + @string.Substring(1);
    }

    /// <summary>
    /// Returns the right portion of the string for the specified length
    /// </summary>
    public static string Right(this string @string, int length)
    {
      if (length <= 0 || @string.Length == 0)
      {
        return string.Empty;
      }
      if (@string.Length <= length)
      {
        return @string;
      }
      return @string.Substring(@string.Length - length, length);
    }

    /// <summary>
    /// Returns the left portion of the string for the specified length
    /// </summary>
    public static string Left(this string @string, int length)
    {
      if (length <= 0 || @string.Length == 0)
      {
        return string.Empty;
      }
      if (@string.Length <= length)
      {
        return @string;
      }
      return @string.Substring(0, length);
    }

    // TODO rename to Substring?
    // TODO add the LeftOf extension
    // TODO add RightOfLeftOf
    // TODO add Char overloads
    // TODO add Inclusive/Exclusive options
    // TODO add check validity of startIndex
    // TODO length??
    // TODO support Regex and autoregex detection

    /// <summary>
    /// Returns the substring of a string starting after the first match of the specified string.
    /// Parameters specify the starting search position and the type of search to be used for the string.
    /// (Added 2009-03-11 by Jacco)
    /// </summary>
    /// <param name="source"></param>
    /// <param name="value">The System.String to seek.</param>
    /// <param name="startIndex">The search starting position.</param>
    /// <param name="comparisonType">The search type to use.</param>
    /// <returns>A System.String</returns>
    public static string RightOf(this string source, string value, int startIndex, StringComparison comparisonType)
    {
      if (value == null)
        throw new ArgumentNullException("value");
      if (source == null)
        throw new ArgumentNullException("source");
      if (startIndex > source.Length)
        throw new ArgumentOutOfRangeException("startIndex", ResourceStrings.GetString("ArgumentOutOfRange_StartIndexLessThanLength"));
      if (startIndex < 0)
        throw new ArgumentOutOfRangeException("startIndex", ResourceStrings.GetString("ArgumentOutOfRange_StartIndex"));
      // TODO shortcut exit on source/value length == 0
      string result = null;
      if (source != null)
      {
        var startPos = source.IndexOf(value, startIndex, comparisonType);
        if (startPos >= 0)
        {
          startPos = startPos + value.Length;
          result = source.Substring(startPos);
        }
      }
      return result;
    }

    /// <summary>
    /// Returns the substring of a string starting after the first match of the specified string.
    /// Parameters specify the starting search position.
    /// (Added 2009-03-11 by Jacco)
    /// </summary>
    /// <param name="source"></param>
    /// <param name="value">The System.String to seek.</param>
    /// <param name="startIndex">The search type to use.</param>
    /// <returns>A System.String</returns>
    public static string RightOf(this string source, string value, int startIndex)
    {
      return source.RightOf(value, startIndex, StringComparison.CurrentCulture);
    }

    /// <summary>
    /// Returns the substring of a string starting after the first match of the specified string.
    /// Parameters specify the type of search to be used for the string.
    /// (Added 2009-03-11 by Jacco)
    /// </summary>
    /// <param name="source"></param>
    /// <param name="value">The System.String to seek.</param>
    /// <param name="comparisonType">The search type to use.</param>
    /// <returns>A System.String</returns>
    public static string RightOf(this string source, string value, StringComparison comparisonType)
    {
      return source.RightOf(value, 0, comparisonType);
    }

    /// <summary>
    /// Returns the substring of a string starting after the first match of the specified string.
    /// (Added 2009-03-11 by Jacco)
    /// </summary>
    /// <param name="source"></param>
    /// <param name="value">The System.String to seek.</param>
    /// <returns>A System.String</returns>
    public static string RightOf(this string source, string value)
    {
      return source.RightOf(value, 0, StringComparison.CurrentCulture);
    }

    /// <summary>
    /// new
    /// </summary>
    /// <param name="source"></param>
    /// <param name="maxLength"></param>
    /// <returns></returns>
    public static string Truncate(this string source, int maxLength)
    { 
      // todo < 0 should return exeption?
      // todo null should return null or exception?
      // todo remove "..." (Clip would be better)
      if (string.IsNullOrEmpty(source) || maxLength <= 0)
        return string.Empty;
      else if (source.Length > maxLength)
        return source.Substring(0, maxLength) + "...";
      else
        return source;
    }
  }
}
